package com.lp.article.service.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.lp.article.service.IArticleService;
import com.lp.article.service.ILikeService;
import com.lp.feign.ArticleClients;
import com.lp.feign.UserClients;
import com.lp.model.config.AppResult;
import com.lp.model.config.CodeMsg;
import com.lp.model.constants.MQConstants;
import com.lp.model.constants.RedisConstants;
import com.lp.model.dto.article.LikeDto;
import com.lp.model.dto.article.PageDto;
import com.lp.model.entity.article.redis.LikesMsg;
import com.lp.model.entity.article.redis.vo.LikesMsgVO;
import com.lp.model.entity.article.vo.ArticleESVO;
import com.lp.model.entity.article.vo.PageVO;
import com.lp.model.exception.ApplicationException;
import com.lp.model.utils.PageUtils;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class LikeService implements ILikeService {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private UserClients userClients;

    @Autowired
    private IArticleService articleService;

    @Override
    public void like(Long userId, LikeDto likeDto) {
        //1.飞控校验
        if(userId == null || likeDto == null || likeDto.getArticleId() == null) {
            log.info(CodeMsg.FAIL_NULL_LIKE.toString());
            throw new ApplicationException(AppResult.fail(CodeMsg.FAIL_NULL_LIKE));
        }
        //2.通过 mq 实现异步点赞
        mqSendLikeMsg(userId, likeDto);
    }

    @SneakyThrows
    @Override
    public PageVO<LikesMsgVO> getMsgPage(PageDto pageDto) {
        int limit = pageDto.getPageSize();
        int offset = (pageDto.getCurPage() - 1) * pageDto.getPageSize();
        //1.从 redis 上获取点赞消息分页列表
        //1) 获取 json 列表
        List<String> likeMsgVOPageJson = redisTemplate.opsForList().range(RedisConstants.LIKE_MSG_LIST + pageDto.getId(),
                offset, limit);
        if(likeMsgVOPageJson == null) {
            return PageUtils.getPageVO(new ArrayList<>(), 0L);
        }
        //2) 转化
        List<LikesMsgVO> likeMsgVOPage = getLikeMsgVOPageByJson(likeMsgVOPageJson);
        //3) 获取总数
        Long total = redisTemplate.opsForList().size(RedisConstants.LIKE_MSG_LIST + pageDto.getId());
        //2.更新用户消息状态(已读)
        redisTemplate.opsForSet().remove(RedisConstants.USER_LIKE_MSG_STATE_SET,
                pageDto.getId().toString());
        //3.设置过期时间(一旦用户长时间不看，自动删除缓存)
        redisTemplate.expire(RedisConstants.LIKE_MSG_LIST + pageDto.getId(),
                RedisConstants.UPDATE_MSG_EXPIRE, TimeUnit.DAYS);
        //4.整合 VO
        return PageUtils.getPageVO(likeMsgVOPage, total);
    }

    /**
     * 将 json 数据转化 点赞消息分页列表
     */
    @SneakyThrows
    private List<LikesMsgVO> getLikeMsgVOPageByJson(List<String> likeMsgVOPageJson) {
        List<LikesMsgVO> result = new ArrayList<>();
        for(String likeMsgVO : likeMsgVOPageJson) {
            result.add(objectMapper.readValue(likeMsgVO, LikesMsgVO.class));
        }
        return result;
    }

    @SneakyThrows
    @Override
    public void addMsg(LikesMsg likesMsg) {
        //1.在 redis 上增加点赞消息(约定左端消息最新)
        //1) 获取最终 VO
        LikesMsgVO likesMsgVO = getLikeMsgVO(likesMsg);
        //2) 存储到 redis
        redisTemplate.opsForList().leftPush(RedisConstants.LIKE_MSG_LIST + likesMsg.getUserId(),
                objectMapper.writeValueAsString(likesMsgVO));
        //2.更新用户消息状态(存不存在都直接添加即可，因为 set 里没有重复元素)
        redisTemplate.opsForSet().add(RedisConstants.USER_LIKE_MSG_STATE_SET,
                likesMsg.getUserId().toString());
        log.info("用户获得了新的点赞! {}", likesMsg);
    }

    /**
     * 获取点赞消息的最终 VO(保存到 redis 上的数据)
     * @param likesMsg 点赞消息 id 相关数据
     */
    @SneakyThrows
    private LikesMsgVO getLikeMsgVO(LikesMsg likesMsg) {
        LikesMsgVO result = new LikesMsgVO();
        BeanUtils.copyProperties(likesMsg, result);
        //1.获取 ESVO 数据(Article + User(nickname + photo))
        ArticleESVO articleESVO = articleService.getArticleESVO(likesMsg.getPostId());
        if(articleESVO == null) {
            throw new ApplicationException(AppResult.fail(CodeMsg.FAIL_ERROR_ARTICLE_ID));
        }
        //2.整合 VO
        result.setNickName(articleESVO.getNickname());
        result.setPhoto(articleESVO.getPhotoUrl());
        result.setArticleTitle(articleESVO.getTitle());
        return result;
    }

    @Override
    public void delMsgAll(Long userId) {
        redisTemplate.delete(RedisConstants.LIKE_MSG_LIST + userId);
    }

    @Override
    public boolean isRead(Long userId) {
        return !Boolean.TRUE.equals(redisTemplate.opsForSet().
                isMember(RedisConstants.USER_LIKE_MSG_STATE_SET, userId.toString()));
    }

    /**
     * 通过 mq 实现异步点赞
     * @param userId
     * @param likeDto
     */
    @SneakyThrows
    private void mqSendLikeMsg(Long userId, LikeDto likeDto) {
        HashMap<String, Object> map = new HashMap<>();
        map.put("userId", userId);
        map.put("articleId", likeDto.getArticleId());
        String msg = objectMapper.writeValueAsString(map);
        rabbitTemplate.convertAndSend(MQConstants.ARTICLE_EXCHANGE_DIRECT, MQConstants.ARTICLE_LIKE_QUEUE, msg);
    }

}
